package cn.cj.erp.serviceimpl.system;

import cn.cj.erp.common.enums.StatusEnum;
import cn.cj.erp.common.exception.ServiceException;
import cn.cj.erp.common.utils.Assert;
import cn.cj.erp.common.utils.Convert;
import cn.cj.erp.bo.system.SysDeptBo;
import cn.cj.erp.bo.system.SysRoleBo;
import cn.cj.erp.bo.system.SysUserBo;
import cn.cj.erp.common.utils.UserUtils;
import cn.cj.erp.service.system.SysDeptService;
import cn.cj.erp.service.system.SysRoleService;
import cn.cj.erp.service.system.SysUserRoleService;
import cn.cj.erp.service.system.SysUserService;
import cn.cj.erp.serviceimpl.base.ServiceImpl;
import cn.cj.erp.utils.PermissionUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.cj.erp.common.Page;
import cn.cj.erp.common.utils.encrypt.EncryptUtil;
import cn.cj.erp.entity.system.SysUser;
import cn.cj.erp.dao.system.SysUserDao;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 系统用户
 *
 * @author chenjian
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUser, SysUserBo>
        implements SysUserService {
    @Resource
    private SysUserDao sysUserDao;
    @Resource
    private SysUserRoleService sysUserRoleService;
    @Resource
    private SysRoleService sysRoleService;
    @Resource
    private SysDeptService sysDeptService;

    @Override
    public SysUserDao getBaseMapper() {
        return sysUserDao;
    }

    @Override
    public SysUser convert2Entity(SysUserBo sysUserBo) {
        return Convert.convert(sysUserBo, SysUser.class);
    }

    @Override
    public SysUserBo convert2Bo(SysUser sysUser) {
        return Convert.convert(sysUser, SysUserBo.class);
    }

    @Override
    public Page<SysUserBo> queryPage(SysUserBo params) {
        // 部门处理，本部门和及其子部门
        if (params.getDeptId() != null) {
            List<Long> deptIds = sysDeptService.listAllChildIds(params.getDeptId());
            params.setDeptIds(deptIds);
        }
        if (StrUtil.isNotBlank(params.getName())) {
            params.setNameSearch(EncryptUtil.searchEncrypt(params.getName()));
        }
        if (StrUtil.isNotBlank(params.getPhone())) {
            params.setPhone(EncryptUtil.encrypt(params.getPhone()));
        }
        if (StrUtil.isNotBlank(params.getEmail())) {
            params.setEmail(EncryptUtil.encrypt(params.getEmail()));
        }

        Page<SysUserBo> page = super.queryPage(params);
        addDeptName(page.getList());
        for (SysUserBo bo : page.getList()) {
            bo.decrypt();
            bo.hide();
        }
        return page;
    }

    @Override
    public List<SysUserBo> queryList(SysUserBo params) {
        if (params.getDeptId() != null) {
            List<Long> deptIds = sysDeptService.listAllChildIds(params.getDeptId());
            params.setDeptIds(deptIds);
        }
        if (StrUtil.isNotBlank(params.getName())) {
            params.setNameSearch(EncryptUtil.searchEncrypt(params.getName()));
        }
        if (StrUtil.isNotBlank(params.getPhone())) {
            params.setPhone(EncryptUtil.encrypt(params.getPhone()));
        }
        if (StrUtil.isNotBlank(params.getEmail())) {
            params.setEmail(EncryptUtil.encrypt(params.getEmail()));
        }
        params.setNameSearch(params.getName());
        List<SysUserBo> list = super.queryList(params);
        addDeptName(list);
        for (SysUserBo bo : list) {
            bo.decrypt();
        }
        return list;
    }

    @Override
    public String listUserRoleGroup(Long id) {
        List<Long> roleIds = sysUserRoleService.listRoleIdsByUserId(id);
        if (CollectionUtil.isEmpty(roleIds)) {
            return "";
        }
        List<SysRoleBo> roleList = sysRoleService.listByIds(roleIds);
        StringJoiner joiner = new StringJoiner(",");
        for (SysRoleBo sysRole : roleList) {
            joiner.add(sysRole.getRoleName());
        }
        return joiner.toString();
    }

    @Override
    public List<SysUserBo> listByIds(List<Long> ids) {
        List<SysUserBo> list = super.listByIds(ids);
        if (CollectionUtil.isEmpty(list)) {
            return list;
        }
        addDeptName(list);
        for (SysUserBo bo : list) {
            bo.decrypt();
            bo.hide();
        }
        return list;
    }

    @Override
    public SysUserBo getById(Long id) {
        SysUserBo bo = super.getById(id);
        if (bo == null) {
            return null;
        }
        SysDeptBo dept = sysDeptService.getById(bo.getDeptId());
        if (dept != null) {
            bo.setDeptName(dept.getName());
        }
        bo.decrypt();
        bo.hide();
        return bo;
    }

    @Override
    public SysUserBo getByUsername(String username) {
        SysUser sysUser = lambdaQuery().eq(SysUser::getUsername, username).one();
        if (sysUser == null) {
            return null;
        }
        SysUserBo bo = convert2Bo(sysUser);
        bo.decrypt();
        List<Long> roleIds = sysUserRoleService.listRoleIdsByUserId(bo.getUserId());
        if (CollectionUtil.isNotEmpty(roleIds)) {
            bo.setRoles(sysRoleService.listByIds(roleIds));
            bo.setRoleIds(roleIds);
        }
        SysDeptBo sysDept = sysDeptService.getById(bo.getDeptId());
        if (sysDept != null) {
            bo.setDeptName(sysDept.getName());
        }
        return bo;
    }

    @Override
    public Set<String> listMenuPermById(Long id) {
        List<String> perms = getBaseMapper().listMenuPermById(id);
        return PermissionUtil.handlePermission(perms);
    }

    @Override
    public Set<String> listRolePermById(Long id) {
        List<String> perms = getBaseMapper().listRolePermById(id);
        return PermissionUtil.handlePermission(perms);
    }

    @Override
    public List<Long> listMenuIdById(Long id) {
        return getBaseMapper().listMenuIdById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean save(SysUserBo user) {
        Assert.notNull(user, "用户不能为空");
        user.setCreateTime(new Date());
        user.setModifyTime(new Date());
        user.setLastPassTime(new Date());
        user.encrypt();
        boolean result = super.save(user);
        if (!result) {
            log.error("SysUserServiceImpl saveUser 保存失败");
            return false;
        }
        if (CollectionUtil.isNotEmpty(user.getRoleIds())) {
            //保存用户与角色关系
            result = sysUserRoleService.saveAllRolesToUser(user.getUserId(), user.getRoleIds());
            if (!result) {
                // 打上回滚标记，表示需要进行回滚
                rollback();
                return false;
            }
        }
        return true;
    }


    @Override
    public boolean saveBatch(List<SysUserBo> list) {
        for (SysUserBo bo : list) {
            bo.encrypt();
        }
        return super.saveBatch(list);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateById(SysUserBo user) {
        Assert.notNull(user, "用户不能为空");
        if (StrUtil.isBlank(user.getPassword())) {
            user.setPassword(null);
        } else {
            user.setLastPassTime(new Date());
        }
        user.setModifyTime(new Date());
        user.encrypt();
        boolean result = super.updateById(user);
        if (!result) {
            log.error("更新用户失败");
            return false;
        }
        //保存用户与角色关系
        result = sysUserRoleService.saveAllRolesToUser(user.getUserId(), user.getRoleIds());
        if (!result) {
            // 设置回滚表示为true，当真正提交时会回滚事务
            rollback();
            return false;
        }
        return true;
    }

    @Override
    public boolean updateBatchById(List<SysUserBo> list) {
        for (SysUserBo bo : list) {
            bo.encrypt();
        }
        return super.updateBatchById(list);
    }

    @Override
    public boolean updateLogin(SysUserBo user) {
        SysUserBo bo = new SysUserBo();
        bo.setUserId(user.getUserId());
        bo.setLoginIp(user.getLoginIp());
        bo.setLoginTime(new Date());
        return super.updateById(user);
    }

    @Override
    public boolean updatePassword(Long id, String password, String newPassword) {
        SysUser db = lambdaQuery().eq(SysUser::getUserId, id).one();
        if (db == null) {
            log.error("未找到对应用户");
            return false;
        }
        if (!db.getPassword().equals(EncryptUtil.passwordEncrypt(password))) {
            log.error("sm3密码匹配失败");
            return false;
        } else {
            log.info("sm3密码匹配成功");
        }
        SysUserBo bo = new SysUserBo();
        bo.setUserId(id);
        bo.setPassword(EncryptUtil.passwordEncrypt(newPassword));
        bo.setLastPassTime(new Date());
        bo.setModifyTime(new Date());
        return super.updateById(bo);
    }

    @Override
    public boolean updateStatusById(Long userId, StatusEnum status) {
        return lambdaUpdate()
                .eq(SysUser::getUserId, userId)
                .set(SysUser::getStatus, status)
                .update();
    }

    /**
     * 重置用户状态和最后改密时间
     *
     * @param userId 主键
     */
    @Override
    public boolean resetUserById(Long userId) {
        return lambdaUpdate()
                .set(SysUser::getStatus, StatusEnum.NORMAL)
                .set(SysUser::getLastPassTime, new Date())
                .eq(SysUser::getUserId, userId)
                .update();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeById(Long id) {
        // 删除用户角色
        sysUserRoleService.removeByUserId(id);
        boolean result = super.removeById(id);
        if (!result) {
            rollback();
            return false;
        }
        return true;
    }

    @Override
    public boolean checkUsernameUnique(SysUserBo bo) {
        int count = lambdaQuery()
                .ne(ObjectUtil.isNotNull(bo.getUserId()), SysUser::getUserId, bo.getUserId())
                .eq(SysUser::getUsername, bo.getUsername())
                .count();
        return count <= 0;
    }

    @Override
    public boolean checkEmailUnique(SysUserBo bo) {
        // 加密字段，需要加密后查询。
        String email = EncryptUtil.encrypt(bo.getEmail());
        int count = lambdaQuery()
                .ne(ObjectUtil.isNotNull(bo.getUserId()), SysUser::getUserId, bo.getUserId())
                .eq(SysUser::getEmail, email)
                .count();
        return count <= 0;
    }

    @Override
    public boolean checkPhoneUnique(SysUserBo bo) {
        // 加密字段，需要加密后查询。
        String phone = EncryptUtil.encrypt(bo.getPhone());
        int count = lambdaQuery()
                .ne(ObjectUtil.isNotNull(bo.getUserId()), SysUser::getUserId, bo.getUserId())
                .eq(SysUser::getPhone, phone)
                .count();
        return count <= 0;
    }

    @Override
    public boolean checkDeptExistById(Long deptId) {
        return lambdaQuery().eq(SysUser::getDeptId, deptId).count() > 0;
    }

    @Override
    public boolean updateStatusByUsername(String username, StatusEnum status) {
        Assert.notEmpty(username, "请选择用户");
        Assert.notNull(status, "请输入状态");
        SysUserBo userBo = getByUsername(username);
        Assert.notNull(userBo, "用户不存在");
        if (UserUtils.isAdmin(userBo.getUserId())) {
            throw new ServiceException("超级管理员用户不允许被修改状态");
        }
        return this.updateStatusById(userBo.getUserId(), status);
    }

    /**
     * 补充部门名称
     * @param list 用户列表
     */
    private void addDeptName(List<SysUserBo> list) {
        // 列表为空，不进行处理
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        // 从用户列表中提取出部门ID
        Set<Long> deptIds = list.stream().map(SysUserBo::getDeptId).collect(Collectors.toSet());
        if (CollectionUtil.isEmpty(deptIds)) {
            return;
        }
        List<SysDeptBo> sysDeptList = sysDeptService.listByIds(ListUtil.list(false, deptIds));
        Map<Long, String> map = sysDeptList.stream().collect(Collectors.toMap(SysDeptBo::getDeptId, SysDeptBo::getName));
        for (SysUserBo sysUser : list) {
            if (map.containsKey(sysUser.getDeptId())) {
                sysUser.setDeptName(map.get(sysUser.getDeptId()));
            }
        }
    }
}
